<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>注册</h1>
<label>
    <input type="checkbox" id="legal">我已阅读相关说明并遵守相关法律</label>
<form id="register">
    <div>
        用户名:
        <input type="text" name="username">
        <br> 未来意向：
        <label><input type="radio" name="purp" value="1">Java工程师</label>
        <label><input type="radio" name="purp" value="2">测试工程师</label>
        <label><input type="radio" name="purp" value="3">前端工程师</label>
        <!-- 添加隐藏域 -->
        <input type="hidden" name="purpose">
        <br>
        <br> 请从以下课程中选择2项最喜欢的课程
        <ul>
            <li>
                <label>
                    <input type="checkbox" name="courses">Web开发技术
                </label>
            </li>
            <li>
                <label>
                    <input type="checkbox" name="courses">软件项目管理
                </label>
            </li>
            <li>
                <label>
                    <input type="checkbox" name="courses">数据库原理
                </label>
            </li>
            <li>
                <label>
                    <input type="checkbox" name="courses">系统分析与设计
                </label>
            </li>
        </ul>
        地址：
        <ul id="ul_address">
        </ul>
        <input name="address">
        <button type="button" id="button_address">添加地址</button>
        <br>
    </div>
    <button type="submit">提交</button>
</form>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script>
    $(function () {
        /* ---------------初始化---------------------- */
        // 初始化时，所有输入全部不可用，基于id，后代，伪类选择器
        // 不支持直接给form设置disabled
        const registerForm = $("#register :input").prop("disabled", true);
        // 提交按钮状态，由2个条件决定，将2个状态设为变量，便于后续修改使用
        // 用户名长度校验标识
        let userNameValid = false;
        //课程数量校验标识
        let coursesValid = false;
        /* --------------------------------------- */
        /* 同意协议复选框 */
        $("#legal").click(function () {
            const checked = $(this).prop("checked");
            // 同意，表单内为可用，但提交按钮由独立条件判断
            registerForm.not($("button:submit")).prop("disabled", !checked);
            // 同意，还需判断选项组状态
            checked && checkBoxs()
        });
        /* 去空格后，判断输入长度 */
        $("input[name=username]").change(function () {
            if ($(this).val().trim().length < 6) {
                alert("用户名长度必须大于等于6");
                // 不符合规则
                userNameValid = false;
            } else {
                // 符合规则标识
                userNameValid = true;
            }
        });

        /* 动态添加地址 */
        $("#button_address").click(() => {
            const input = $("input[name=address]");
            const item = $(`<li>${input.val()}</li>`);
            input.val("");
            item.css("display", "none");
            $("#ul_address").append(item);
            item.fadeIn(1000);
        });

        /* checkbox checked数量状态控制 */
        const amount = 2;
        // name=courses的复选框组
        const checkboxes = $("input[name=courses]");
        $(checkboxes).change(() => checkBoxs());
        // 需要多处判断，声明为独立函数
        function checkBoxs() {
            // 过滤，复选框组中所有，选中项
            const checked = checkboxes.filter(":checked");
            // 过滤，复选框组中所有，未选择项
            const unChecked = checkboxes.not(":checked");
            // 比较数量，符合数量，修改标识
            coursesValid = checked.length >= amount;
            // 绑定未选中项状态
            unChecked.prop("disabled", coursesValid);
        }

        /* 同时监听2个状态的改变，但无需重写以上校验代码，仅需判断之前校验产生的状态标识即可 */
        $("input[name=username], input[name=courses]").change(() => {
            const dis = userNameValid && coursesValid;
            // 当2个状态均符合规范时，提交按钮可用
            $("button:submit").prop("disabled", !dis);
        });

        /* 无法基于radio checked状态判断，因为每次点击radio时该radio均为checked状态
        * 因此，可通过2种方法实现
        * 方法一，添加隐藏输入框，用于存放单选框值，点击单选框时判断值是否与隐藏域相等
        * 不相等，则为第一次点击，将单选框值赋给隐藏框
        * 相等，则为第二次点击，置被点击单选框为未选中状态，重置隐藏域值 */
        /* 基于隐藏输入框的实现，即表单提交时，传递的是隐藏输入框的值，而非单选框的值 */
        // 可复用的点选框组
        const radios = $("input[name=purp]");
        // 隐藏输入框
        const hiddenInput = $("input[name=purpose]");
        radios.click(function () {
            // 相等，为第二次点击，置此单选框为未选中状态，重置隐藏域值
            if ( $(this).val() === hiddenInput.val()) {
                hiddenInput.val(0);
                $(this).prop("checked", false);
            } else {
                // 不等，为第一次点击，将单选框值赋给隐藏域
                hiddenInput.val($(this).val());
            }
        });

        /* 方法二，不使用隐藏输入框的实现方法，可在不添加html代码的情况下用js解决
        * 为元素添加自定义属性data-*，通过自定义属性设置是否为第二次点击 */
        /*radios.click(function () {
            let secClick = $(this).data("value");
            if (secClick) {
                // 自定义属性值为true，第二次点击
                // 置当前单选框为未选中
                $(this).prop("checked", false);
                // 置标识为false
                $(this).data("value", false);
            } else {
                // 自定义属性无值，为第一次点击，置已点击标识为true
                $(this).data("value", true);
                // 将其他radio标识置为false，为什么？
                radios.not(this).data("value", false);
            }
        });*/
    })
</script>

</body>
</html>